package com.softserveinc.edu.oms.persistence.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Restrictions;

import com.softserveinc.edu.oms.persistence.AbstractEntity;

public class HibernateDao<T extends AbstractEntity> implements Dao<T> {

	protected final Class<T> persistentClass;
	protected final SessionFactory sessionFactory;

	protected Session session;
	protected Transaction transaction;

	public HibernateDao(final Class<T> persistentClass,
			final SessionFactory sessionFactory) {
		this.persistentClass = persistentClass;
		this.sessionFactory = sessionFactory;
	}

	protected void commitTransactionAndCloseSession() {
		transaction.commit();
		session.close();
	}

	protected void openSessionAndBeginTransaction() {
		session = sessionFactory.openSession();
		transaction = session.beginTransaction();
	}

	@Override
	public List<T> findAll() {
		return findByCriterions();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByCriterions(final Criterion... criterions) {
		openSessionAndBeginTransaction();

		Criteria criteria = session.createCriteria(persistentClass);

		for (Criterion criterion : criterions) {
			if (criterion != null) {
				criteria.add(criterion);
			}
		}

		List<T> entities = criteria.list();

		commitTransactionAndCloseSession();

		return entities;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByOneOfCriterions(final Criterion... criterions) {
		openSessionAndBeginTransaction();

		Criteria criteria = session.createCriteria(persistentClass);

		Disjunction disjunction = Restrictions.disjunction();
		for (Criterion criterion : criterions) {
			if (criterion != null) {
				disjunction.add(criterion);
			}
		}

		criteria.add(disjunction);

		List<T> entities = criteria.list();

		commitTransactionAndCloseSession();

		return entities;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findByID(final Integer id) {
		if (id == null) {
			return null;
		}

		T entity = null;

		openSessionAndBeginTransaction();

		entity = (T) session.get(persistentClass, id);

		commitTransactionAndCloseSession();

		return entity;
	}

	@Override
	public T insertOrUpdate(final T entity) {
		if (entity.getId() == null) {
			return insert(entity);
		} else {
			return update(entity);
		}
	}

	@Override
	public void delete(final T entity) {
		openSessionAndBeginTransaction();

		session.delete(entity);

		commitTransactionAndCloseSession();
	}

	private T insert(final T entity) {
		openSessionAndBeginTransaction();

		Integer id = (Integer) session.save(entity);
		entity.setId(id);

		commitTransactionAndCloseSession();

		return entity;
	}

	private T update(final T entity) {
		openSessionAndBeginTransaction();

		session.update(entity);

		commitTransactionAndCloseSession();

		return entity;
	}
}
